package cc.yaomm.gateway.supply.filter;

import cc.yaomm.common.basic.constant.DefaultConstant;
import cc.yaomm.common.basic.domain.Response;
import cc.yaomm.common.basic.enums.ResponseCode;
import cc.yaomm.common.biz.lang.bo.LoginBO;
import cc.yaomm.common.basic.util.TokenUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;

/**
 * Created by XQL on 2021/5/25.
 * 登录验证
 */
@Slf4j
public class TokenFilter<T extends LoginBO> implements GlobalFilter, Ordered {

    //用户登录信息
    private Class<T> clazz;

    //token工具类
    private TokenUtils tokenUtils;

    public TokenFilter(Class clazz, TokenUtils tokenUtils) {
        this.clazz = clazz;
        this.tokenUtils = tokenUtils;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1. 获取请求
        ServerHttpRequest request = exchange.getRequest();
        //2. 获取响应
        ServerHttpResponse response = exchange.getResponse();
        //3. 排除登录路径
        if (request.getURI().getPath().contains("/login") ||
                request.getURI().getPath().contains("/common")) {
            return chain.filter(exchange);
        }

        //4. 获取请求头中的令牌
        HttpHeaders headers = request.getHeaders();
        String tokenStr = headers.getFirst(DefaultConstant.HEADER_TOKEN);

        //判断客户端是否传递了TOKEN
        if (!StringUtils.hasText(tokenStr) || "null".equalsIgnoreCase(tokenStr)) {
            Response resp = Response.fail(ResponseCode.FAILED, "请登录后继续访问");
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return response.writeWith(Flux.just(resp.toString()).map(x -> response.bufferFactory().wrap(x.getBytes())));
        }

        //token不为空
        String tokenValue = null;
        try {
            tokenValue = tokenUtils.getToken(tokenStr);
        } catch (Exception e) {
            log.error("解析token异常", e);
        }

        if (!StringUtils.hasText(tokenValue)) {
            Response resp = Response.fail(ResponseCode.FAILED, "登录信息已失效，请重新登录");
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return response.writeWith(Flux.just(resp.toString()).map(x -> response.bufferFactory().wrap(x.getBytes())));
        }

        //验证TOKEN通过
        try {
            LoginBO loginBO = JSON.parseObject(tokenValue, clazz);
            if (StringUtils.hasText(loginBO.getMessage())) {
                Response resp = Response.fail(ResponseCode.FAILED, loginBO.getMessage());
                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                return response.writeWith(Flux.just(resp.toString()).map(x -> response.bufferFactory().wrap(x.getBytes())));
            }

            //请求头添加用户信息
            request = request.mutate()
                    .header(DefaultConstant.USER_LOGIN_INFO, URLEncoder.encode(tokenValue, DefaultConstant.UTF8))
                    .header(DefaultConstant.REQUEST_UID, String.valueOf(loginBO.getUserId()))
                    .header(DefaultConstant.REQUEST_START_TIME, String.valueOf(System.currentTimeMillis())).build();

            return chain.filter(exchange.mutate().request(request).build());
        } catch (Exception e) {
            log.warn("解析用户登录信息失败，token:[{}],value:[{}]", tokenStr, tokenValue);
            String msg = tokenValue.startsWith("系统") ? tokenValue : "登录信息已失效，请重新登录";
            tokenUtils.clearToken(tokenStr);
            Response resp = Response.fail(ResponseCode.FAILED, msg);
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return response.writeWith(Flux.just(resp.toString()).map(x -> response.bufferFactory().wrap(x.getBytes())));
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }
}
